package uplb.ics.cs190.e2d.display;

import java.awt.DisplayMode;
import java.awt.Frame;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.awt.image.BufferStrategy;
import java.awt.image.VolatileImage;
import java.util.ArrayList;

import uplb.ics.cs190.e2d.graphics.Color;
import uplb.ics.cs190.e2d.graphics.Image;
import uplb.ics.cs190.e2d.math.Dimension;

public class FullScreenMode implements Display {

	/**
	 * The graphics device that constructs this graphics engine. 
	 */
	public static final GraphicsDevice DEVICE = GraphicsEnvironment.getLocalGraphicsEnvironment()
			.getDefaultScreenDevice();
	
	/**
	 * The graphics configuration that constructs this graphics engine.
	 */
	public static final GraphicsConfiguration CONFIG = DEVICE.getDefaultConfiguration();
	
	private Frame frame;
	
	private Dimension size;
	
	private VolatileImage offscreen;
	
	private BufferStrategy strategy;
	
	/**
	 * The current graphics context for this <code>WindowedMode</code> display.
	 */
	private J2SEGraphics currentGraphics;

	public FullScreenMode(Dimension d, String title, boolean bufferstrategy) {
		this.size = d;
		this.currentGraphics = new J2SEGraphics();
		
		if(!FullScreenMode.DEVICE.isFullScreenSupported()) {
			throw new RuntimeException("Full Screen Exclusive Mode not supported");
		}
		
		this.frame = new Frame(title, FullScreenMode.CONFIG);
		
		this.frame.setResizable(false);
		this.frame.setIgnoreRepaint(true);
		
		this.frame.setLayout(null);
		this.frame.setUndecorated(true);
		this.frame.dispose();
		
		FullScreenMode.DEVICE.setFullScreenWindow(frame);
		
		if(!FullScreenMode.DEVICE.isDisplayChangeSupported()) {
			FullScreenMode.DEVICE.setFullScreenWindow(null);
			this.frame.dispose();
			throw new RuntimeException("Changing Display Mode is not supported");
		}
		
		DisplayMode bestDisplay = this.getBestDisplayMode(this.size);
		
		if(bestDisplay == null) {
			FullScreenMode.DEVICE.setFullScreenWindow(null);
			this.frame.dispose();
			throw new RuntimeException("Changing Display Mode to " +
					this.size.width + "x" + this.size.height
					+ " is not supported");
		}
		
		FullScreenMode.DEVICE.setDisplayMode(bestDisplay);
		
		try {
			Thread.sleep(1000L);
		} catch(InterruptedException e) {
			
		}
		
		if(bufferstrategy) {
			bufferstrategy = this.createBufferStrategy();
		}
		
		if(!bufferstrategy) {
			this.createBackBuffer();
		}
		
		this.frame.requestFocus();
	}
	
	private void createBackBuffer() {
		if(this.offscreen != null) {
			this.offscreen.flush();
			this.offscreen = null;
		}
		
		this.offscreen = FullScreenMode.CONFIG.createCompatibleVolatileImage(
				this.size.width, this.size.height);
	}

	private boolean createBufferStrategy() {
		boolean bufferCreated;
		int num = 0;
		
		do {
			bufferCreated = true;
			try {
				this.frame.createBufferStrategy(2);
			} catch(Exception e) {
				bufferCreated = false;
				try {
					Thread.sleep(200);
				}
				catch(InterruptedException ex) {
					
				}
			}
			
			if(num++ > 5) {
				break;
			}
		} while(!bufferCreated);
		
		if(bufferCreated) {
			// error
		} else {
			while(this.strategy == null) {
				try {
					this.strategy = this.frame.getBufferStrategy();
				} catch(Exception e) {
					
				}
			}
			
			Graphics gfx = null;
			while(gfx == null) {
				try {
					gfx = getBackBuffer();
				} catch(Exception e) {
					
				}
			}
		}
		
		return false;
	}

	private DisplayMode getBestDisplayMode(Dimension size) {
		DisplayMode mode[] = FullScreenMode.DEVICE.getDisplayModes();
		
		ArrayList<DisplayMode> modeList = new ArrayList<DisplayMode>();
		for(int i=0; i<mode.length; i++) {
			if(mode[i].getWidth() == size.width
					&& mode[i].getHeight() == size.height) {
				modeList.add(mode[i]);
			}
		}
		
		if(modeList.size() == 0) {
			return null;
		}
		
		return modeList.get(modeList.size()-1);
	}
	
	/**
	 * Returns whether this <code>WindowedMode</code> is using a bufferstrategy
	 * or a volatile image.
	 * 
	 * @return	if a buffer strategy or a volatile image is used.
	 */
	public boolean isBufferStrategy() {
		return (this.strategy != null);
	}

	@Override
	public Graphics getBackBuffer() {
		if(this.currentGraphics.getGraphics() == null) {
			if(this.strategy == null) {
				if(this.offscreen.validate(FullScreenMode.CONFIG) == 
						VolatileImage.IMAGE_INCOMPATIBLE) {
					this.createBackBuffer();
				}
				this.currentGraphics.setGraphics(this.offscreen.createGraphics());
			} else {
				this.currentGraphics.setGraphics((java.awt.Graphics2D)
						this.strategy.getDrawGraphics());
			}
		}

		this.currentGraphics.setColor(Color.BLACK);
		this.currentGraphics.fillRect(0, 0, 640, 480);
		
		return this.currentGraphics;
	}

	@Override
	public boolean flip() {
		this.currentGraphics.getGraphics().dispose();
		this.currentGraphics.setGraphics(null);
		
		if(this.strategy == null) {
			this.frame.getGraphics().drawImage(this.offscreen, 0, 0, null);
			Toolkit.getDefaultToolkit().sync();
			return (!this.offscreen.contentsLost());
		} else {
			this.strategy.show();
			Toolkit.getDefaultToolkit().sync();
			return (!this.strategy.contentsLost());
		}
	}

	@Override
	public void cleanup() {
		try {
			Thread.sleep(200L);
		} catch(InterruptedException e) {
			
		}
		
		try {
			if(this.frame != null) {
				this.frame.dispose();
			}
		} catch(Exception e) {
			
		}
	}

	@Override
	public Dimension getSize() {
		return this.size;
	}

	@Override
	public Image getWindowIcon() {
		return null;
	}

	@Override
	public String getWindowTitle() {
		return this.frame.getTitle();
	}

	@Override
	public void setWindowIcon(Image icon) {

	}

	@Override
	public void setWindowTitle(String title) {
		this.frame.setTitle(title);
	}

}
